#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kfifo.h>

#include <linux/ioctl.h>
#include <linux/uaccess.h>

#include "vser.h"

#define VSER_MAJOR    256
#define VSER_MINOR    0
#define VSER_DEV_CNT  1
#define VSER_DEV_NAME "vser"

// 定义虚拟串口数据结构
struct vser_dev {
    unsigned int baud;
    struct option opt;
    struct cdev cdev;
};

DEFINE_KFIFO(vsfifo, char, 1024);
static struct vser_dev vsdev;


static int vser_open(struct inode* inode, struct file *filp)
{
    return 0;
}

static int vser_release(struct inode* inode, struct file *filp)
{
    return 0;
}

static ssize_t vser_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
    unsigned int copied = 0;
    int ret;
    ret = kfifo_to_user(&vsfifo, buf, count, &copied);
    return ret == 0 ? copied : ret;
}

static ssize_t vser_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
{
    unsigned int copied = 0;
    int ret;
    ret = kfifo_from_user(&vsfifo, buf, count, &copied);
    return ret == 0 ? copied : ret;
}

static long vser_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    // 通过_IOC_TYPE取出命令中的幻数字段，和预定的幻数进行比较（校验是否是错误命令数）
    if (_IOC_TYPE(cmd) != VS_MAGIC)
        return -ENOTTY;

    switch (cmd) {
    case VS_SET_BAUD:
        vsdev.baud = arg;
        break;
    case VS_GET_BAUD:
        arg = vsdev.baud;
        break;
    case VS_SET_FFMT:
        if (copy_from_user(&vsdev.opt, (struct option __user *)arg, sizeof(struct option)))
            return -EFAULT;
        break;
    case VS_GET_FFMT:
        if (copy_to_user((struct option __user *)arg, &vsdev.opt, sizeof(struct option)))
            return -EFAULT;
        break;
    default:
        return -ENOTTY;
    }
    return 0;
}
// copy_from_user/copy_to_user比memcpy多个验证用户空间是否真实可读写的步骤，避免内核崩溃
// 除了这俩个还有更方便的函数：get_user、put_user

static struct file_operations vser_ops = {
    .owner = THIS_MODULE,
    .open = vser_open,
    .release = vser_release,
    .read = vser_read,
    .write = vser_write,
    .unlocked_ioctl = vser_ioctl,  // 增加ioctl接口
};

static int __init vser_init(void)
{
    int ret;
    dev_t dev;

    dev = MKDEV(VSER_MAJOR, VSER_MINOR);
    ret = register_chrdev_region(dev, VSER_DEV_CNT, VSER_DEV_NAME);
    if (ret)
        goto reg_err;

    cdev_init(&vsdev.cdev, &vser_ops);
    vsdev.cdev.owner = THIS_MODULE;
    // 初始化数据成员
    vsdev.baud = 115200;
    vsdev.opt.datab = 8;
    vsdev.opt.parity = 0;
    vsdev.opt.stopb = 1;

    ret = cdev_add(&vsdev.cdev, dev, VSER_DEV_CNT);
    if (ret)
        goto add_err;

    return 0;
add_err:
    unregister_chrdev_region(dev, VSER_DEV_CNT);
reg_err:
    return ret;
}

static void __exit vser_exit(void)
{
    dev_t dev;
    dev = MKDEV(VSER_MAJOR, VSER_MINOR);
    cdev_del(&vsdev.cdev);
    unregister_chrdev_region(dev, VSER_DEV_CNT);
}

module_init(vser_init);
module_exit(vser_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("KDY <chaoxi_todo@qq.com>");
MODULE_DESCRIPTION("A simple module");
MODULE_ALIAS("virtual-serial");